package com.maodou.android.viewmodel.account

import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.lifecycle.viewModelScope
import com.maodou.android.data.model.AccountFilterParams
import com.maodou.android.data.model.ProxyInfo
import com.maodou.android.data.remote.AccountRepository
import com.maodou.android.utils.ExcelUtils
import com.maodou.android.utils.FileUtils.getAuthFileFile
import com.maodou.core.base.BaseViewModel
import com.maodou.core.datastore.db.AccountStore
import com.maodou.core.datastore.db.table.AccountEntry
import com.maodou.core.navigation.GlobalUiBus
import com.maodou.core.navigation.Navigator3
import com.maodou.core.network.handleResult
import com.maodou.core.network.toUserMessage
import com.maodou.core.utils.JsonUtils
import com.maodou.core.utils.Logger
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream
import javax.inject.Inject
import kotlin.math.ceil

/**
 * @Author Xing
 * @CreateTime 2025年09月20日
 * @Description 账号相关业务数据处理
 */
@HiltViewModel
class AccountViewModel @Inject constructor(
    @ApplicationContext private val appContext: Context,
    private val repository: AccountRepository,
    private val accountStore: AccountStore
) : BaseViewModel<AccountUiState, AccountUiEvent>() {

    private var totpJob: Job? = null
    private var totpPeriodSeconds: Int = 30
    private var nextTotpRefreshAtMs: Long = 0L
    private var currentTotpCode: String = ""
    private var checkTaskJob: Job? = null

    //记录下一页页码
    private val typePageIndex = mutableMapOf<String, Int>()
    private var currentListType: String? = null

    override fun createInitialState(): AccountUiState {
        return AccountUiState()
    }

    override fun setLoading(isLoading: Boolean) {
        if(isLoading) GlobalUiBus.showLoading("加载中...")
        else GlobalUiBus.hideLoading()
        updateUiState { it.copy(isLoading = isLoading) }
    }

    fun clearTableData() {
        totpJob?.cancel()
        updateUiState { it.copy(header = arrayListOf(), rows = arrayListOf(), searchAccountData = arrayListOf()) }
    }

    fun getTwoFaCode(accountEntry: AccountEntry?){
        viewModelScope.launch {
            repository.getTwoFaCode("${accountEntry?.twoFa}").handleResult(
                onSuccess = {
                    totpPeriodSeconds = it.expireIn
                    val secret = "${accountEntry?.twoFa}"
                    val code = it.code ?: ""
                    if (secret.isNotBlank() && code.isNotBlank()) {
                        startTotpAuto(secret, code, it.expireIn)
                    }
                },
                onError = { error ->
                    Logger.d("请求失败=${error.toUserMessage()}")
                },
                onLoading = { loading ->
                    setLoading(loading)
                    updateUiState {
                        it.copy(
                            isLoading = loading
                        )
                    }
                }
            )
        }
    }

    fun setV2FAResume(isResume:Boolean){
        updateUiState {
            it.copy(
                v2FAResume = isResume
            )
        }
    }

    private fun startTotpAuto(secret: String, initialCode: String, expireInSec: Int) {
        totpJob?.cancel()
        currentTotpCode = initialCode
        totpPeriodSeconds = expireInSec
        nextTotpRefreshAtMs = System.currentTimeMillis() + expireInSec * 1000L
        totpJob = viewModelScope.launch(Dispatchers.Default) {
            while (isActive) {
                val now = System.currentTimeMillis()
                if (now >= nextTotpRefreshAtMs) {
                    if(uiState.value.v2FAResume) repository.getTwoFaCode(secret).handleResult(
                        onSuccess = { result ->
                            val newCode = result.code ?: currentTotpCode
                            val newPeriod = result.expireIn.takeIf { it > 0 } ?: totpPeriodSeconds
                            currentTotpCode = newCode
                            totpPeriodSeconds = newPeriod
                            nextTotpRefreshAtMs = System.currentTimeMillis() + newPeriod * 1000L
                            viewModelScope.launch { emitTotpTickByDeadline(currentTotpCode, nextTotpRefreshAtMs) }
                        },
                        onError = { _ ->
                            nextTotpRefreshAtMs = System.currentTimeMillis() + 2_000L
                        },
                        onLoading = { }
                    )
                } else {
                    emitTotpTickByDeadline(currentTotpCode, nextTotpRefreshAtMs)
                    delay(200)
                }
            }
        }
    }

    private suspend fun emitTotpTickByDeadline(code: String, deadlineMs: Long) {
        val periodMs = (totpPeriodSeconds * 1000L).coerceAtLeast(1000L)
        val now = System.currentTimeMillis()
        val remaining = (deadlineMs - now).coerceAtLeast(0L)
        val progress = (remaining.toFloat() / periodMs).coerceIn(0f, 1f)
        val secondsLeft = ceil(remaining / 1000.0).toInt()
        updateUiState {
            it.copy(
                authCode = code,
                progress = progress,
                secondsLeft = secondsLeft
            )
        }
    }

    /**
     * 读取用户选择的文件（CSV/Excel/TXT）
     */
    fun importAccount(uri: Uri) {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                setLoading(true)
                val mime = appContext.contentResolver.getType(uri) ?: ""
                val displayName = ExcelUtils.getFileName(appContext,uri) ?: ""
                appContext.contentResolver.openInputStream(uri)?.use { input ->
                    val bytes = input.readBytes()
                    val mimeNorm = mime.lowercase()
                    val nameLower = displayName.lowercase()
                    val ext = when {
                        nameLower.endsWith(".csv") -> "csv"
                        nameLower.endsWith(".xlsx") -> "xlsx"
                        nameLower.endsWith(".xls") -> "xls"
                        nameLower.endsWith(".txt") -> "txt"
                        else -> ""
                    }
                    val isCsvMime = mimeNorm == "text/csv" ||
                            mimeNorm == "text/comma-separated-values" ||
                            mimeNorm == "application/csv" ||
                            mimeNorm.contains("csv") ||
                            ((mimeNorm == "text/plain" || mimeNorm == "application/octet-stream") && ext == "csv")
                    val isTxtMime = (mimeNorm.startsWith("text/") || mimeNorm == "application/octet-stream") && ext == "txt"

                    try {
                        when {
                            ext == "csv" || isCsvMime -> {
                                val (header, rows) = ExcelUtils.parseCsv(ByteArrayInputStream(bytes))
                                updateUiState { it.copy(header = header, rows = rows) }
                            }

                            ext == "xlsx" || mimeNorm == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" -> {
                                val (header, rows) = ExcelUtils.parseXlsx(ByteArrayInputStream(bytes))
                                updateUiState { it.copy(header = header, rows = rows) }
                                Logger.d("${header} 列，${rows} 行")
                            }

                            ext == "xls" || mimeNorm == "application/vnd.ms-excel" -> {
                                val (header, rows) = ExcelUtils.parseXls(ByteArrayInputStream(bytes))
                                updateUiState { it.copy(header = header, rows = rows) }
                            }

                            ext == "txt" || isTxtMime -> {
                                val (header, rows) = ExcelUtils.parsePipeTxt(ByteArrayInputStream(bytes))
                                updateUiState { it.copy(header = header, rows = rows) }
                            }

                            else -> {
                                runCatching {
                                    ExcelUtils.parseCsv(ByteArrayInputStream(bytes))
                                }.onSuccess { (header, rows) ->
                                    updateUiState { it.copy(header = header, rows = rows) }
                                }.onFailure {
                                    runCatching {
                                        ExcelUtils.parseXlsx(ByteArrayInputStream(bytes))
                                    }.onSuccess { (header, rows) ->
                                        updateUiState { it.copy(header = header, rows = rows) }
                                    }.onFailure {
                                        runCatching {
                                            ExcelUtils.parseXls(ByteArrayInputStream(bytes))
                                        }.onSuccess { (header, rows) ->
                                            updateUiState { it.copy(header = header, rows = rows) }
                                        }.onFailure {
                                            runCatching {
                                                ExcelUtils.parsePipeTxt(ByteArrayInputStream(bytes))
                                            }.onSuccess { (header, rows) ->
                                                updateUiState { it.copy(header = header, rows = rows) }
                                            }.onFailure {
                                                GlobalUiBus.showToast("不支持的文件类型")
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } finally {
                        setLoading(false)
                    }
                } ?: run {
                    GlobalUiBus.showToast("无法读取文件")
                    setLoading(false)
                }
            } catch (e: Exception) {
                setLoading(false)
                GlobalUiBus.showToast("读取失败：${e.message}")
            }
        }
    }

    /**
     * 保存某一行（按 header 列数对齐），仅更新内存中的 rows，后续可在此处对接 Repository 入库
     */
    fun updateRow(index: Int, newValues: List<String>) {
        updateUiState { state ->
            val rows = state.rows.toMutableList()
            if (index !in rows.indices) return@updateUiState state
            val colCount = state.header.size
            val normalized = if (colCount > 0) {
                MutableList(colCount) { i -> newValues.getOrNull(i) ?: "" }
            } else newValues
            rows[index] = normalized
            GlobalUiBus.showToast("已更新")
            state.copy(rows = rows)
        }
    }

    /**
     * 删除某一行（仅更新内存中的 rows）
     */
    fun removeRow(index: Int) {
        updateUiState { state ->
            val rows = state.rows.toMutableList()
            if (index in rows.indices) {
                rows.removeAt(index)
                state.copy(rows = rows)
            } else state
        }
    }

    /**
     * 根据分类获取分页数据
     */
    fun getAccountList(type: String) {
        viewModelScope.launch {
            val currentFilter = uiState.value.accountFilterParams
            val accountData = if (currentFilter != null && (currentFilter.platforms?.isNotEmpty() == true || currentFilter.statuses?.isNotEmpty()  == true || currentFilter.tags?.isNotEmpty()  == true)) {
                // 有筛选条件时，使用筛选查询
                val platforms = if (currentFilter.platforms?.isNotEmpty()  == true) currentFilter.platforms else null
                val statuses = if (currentFilter.statuses?.isNotEmpty()  == true) currentFilter.statuses else null
                val tags = if (currentFilter.tags?.isNotEmpty()  == true) currentFilter.tags else null
                accountStore.pageByFilter(platforms, statuses, tags, 1, type,20)
            } else {
                // 无筛选条件时，使用原有的按类型查询
                accountStore.pageByType(type, 1, 20)
            }
            
            updateUiState {
                it.copy(
                    accountData = accountData.items,
                    isMore = accountData.hasMore
                )
            }
            currentListType = type
            typePageIndex[type] = 2 // 下一次从第2页开始
        }
    }

    /**
     * 按照筛选条件查询
     */
    fun setAccountFilter(accountFilterParams: AccountFilterParams?){
        updateUiState {
            it.copy(
                accountFilterParams = accountFilterParams
            )
        }
    }

    /**
     * 加载指定分类的指定页；reset=true 时覆盖列表并重置页码
     */
    fun loadPageForType(type: String, page: Int = 1, pageSize: Int = 20, reset: Boolean = false,isRefresh:(Boolean) -> Unit = {}) {
        viewModelScope.launch {
            try {
                val currentFilter = uiState.value.accountFilterParams
                val result = if (currentFilter != null && (
                            currentFilter.platforms?.isNotEmpty() == true
                            || currentFilter.statuses?.isNotEmpty() == true
                            || currentFilter.tags?.isNotEmpty() == true)) {
                    // 有筛选条件时，使用筛选查询
                    val platforms = if (currentFilter.platforms?.isNotEmpty()  == true ) currentFilter.platforms else null
                    val statuses = if (currentFilter.statuses?.isNotEmpty()  == true ) currentFilter.statuses else null
                    val tags = if (currentFilter.tags?.isNotEmpty() == true ) currentFilter.tags else null
                    accountStore.pageByFilter(platforms, statuses, tags, page, type,pageSize)
                } else {
                    // 无筛选条件时，使用原有的按类型查询
                    accountStore.pageByType(type, page, pageSize)
                }
                
                updateUiState { state ->
                    val newList = if (reset || page == 1) result.items else state.accountData + result.items
                    state.copy(
                        accountData = newList,
                        isMore = result.hasMore
                    )
                }
                currentListType = type
                typePageIndex[type] = page + 1
                isRefresh(false)
            } catch (e: Exception) {
                GlobalUiBus.showToast("加载失败：${e.message ?: "未知错误"}")
                isRefresh(false)
            }
        }
    }

    /**
     * 加载指定分类的下一页
     */
    fun loadNextPageForType(type: String, pageSize: Int = 20) {
        val nextPage = typePageIndex[type] ?: 2
        loadPageForType(type, page = nextPage, pageSize = pageSize, reset = false)
    }

    /**
     * 导出账号
     */
    fun saveImportAccount(platform:String,accountType:String,onSuccess:() -> Unit = {}) {
        val header = uiState.value.header
        val rows = uiState.value.rows
        if (rows.isEmpty()) {
            GlobalUiBus.showToast("请先添加账号")
            return
        }
        viewModelScope.launch {
            // 构建列索引（将不固定的表头映射到实体字段）
            val colIndex = ExcelUtils.buildColumnIndex(header)
            val idxAccount = colIndex["accountID"] ?: -1
            if (idxAccount < 0) {
                GlobalUiBus.showToast("缺少必填列：账号ID")
                return@launch
            }
            updateUiState { it.copy(isSave = true) }
            // 生成待保存的 AccountEntry 列表（过滤掉账号ID为空的行）
            val toSave = rows.mapNotNull { row ->
                val accountId = row.getOrNull(idxAccount)?.trim().orEmpty()
                if (accountId.isEmpty()) return@mapNotNull null
                val nickname = row.getOrNull(colIndex["nickname"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val password = row.getOrNull(colIndex["password"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val phone = row.getOrNull(colIndex["phone"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val twoFa = row.getOrNull(colIndex["twoFa"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val email = row.getOrNull(colIndex["email"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val emailPassword = row.getOrNull(colIndex["emailPassword"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val ipCountry = row.getOrNull(colIndex["ipCountry"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val cookie = row.getOrNull(colIndex["cookie"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val authCode = row.getOrNull(colIndex["authCode"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                val tags = row.getOrNull(colIndex["tags"] ?: -1)?.trim().orEmpty().ifEmpty { null }
                AccountEntry(
                    key = "",
                    accountID = accountId,
                    userId = "",
                    nickname = nickname,
                    password = password,
                    phone = phone,
                    twoFa = twoFa,
                    email = email,
                    emailPassword = emailPassword,
                    ipCountry = ipCountry,
                    cookie = cookie,
                    authCode = authCode,
                    type = accountType,
                    platform = platform,
                    tags = tags,
                )
            }
            if (toSave.isEmpty()) {
                GlobalUiBus.showToast("没有可保存的有效账号")
                updateUiState { it.copy(isSave = false) }
                return@launch
            }

            try {
                val ids = accountStore.upsertAll(toSave) // 入库
                GlobalUiBus.showAlertDialog(
                    title = "已保存${ids.size}条账号",
                    positiveText = "知道了",
                    cancelable = false,
                    onConfirm = {
                        Navigator3.pop()
                    }
                )
                updateUiState { it.copy(isSave = false) }
                onSuccess()
            } catch (e: Exception) {
                GlobalUiBus.showToast("保存失败: ${e.message ?: "未知错误"}")
                updateUiState { it.copy(isSave = false) }
            }
        }
    }

    /**
     * 长按列表选择账号
     */
    fun onSelectAccount(account:AccountEntry? = null){
        val current = uiState.value.accountData
        val updated: List<AccountEntry>
        val isAll: Boolean
        if(account == null){
            val allSelected = current.none { !it.isSelect }
            updated = current.map { it.copy(isSelect = !allSelected) }
            isAll = !allSelected
        } else {
            updated = current.map { if (account.key == it.key) it.copy(isSelect = !account.isSelect) else it }
            isAll = updated.none { !it.isSelect }
        }
        updateUiState { it.copy(
            accountData = updated,
            isAllAccount = isAll,
        ) }
    }

    /**
     * 删除账号
     */
    fun onDeleteAccount(accounts:List<AccountEntry>,onBlock:() -> Unit = {}){
        GlobalUiBus.showAlertDialog(
            title = "是否确认删除${accounts.size}个账号？",
            positiveText = "确认删除",
            negativeText = "取消",
            cancelable = true,
            onConfirm = {
                viewModelScope.launch {
                    val ids = accounts.map { it.accountID }
                    val count = accountStore.deleteMany(accountIDs = ids)
                    updateUiState { state ->
                        val idSet = ids.toHashSet()
                        val newList = state.accountData.filterNot { it.accountID in idSet }
                        val isAll = newList.isNotEmpty() && newList.none { !it.isSelect }
                        state.copy(
                            accountData = newList,
                            isAllAccount = isAll,
                        )
                    }
                    onBlock()
                    GlobalUiBus.showToast("成功删除${count}个账号")
                }
            }
        )
    }

    // 启动批量检查任务（串行）
    fun startCheckSelectedAccounts(accounts: List<AccountEntry>) {
        // 若已有任务在执行，直接提示并返回
        if (checkTaskJob?.isActive == true) {
            GlobalUiBus.showToast("有检查任务正在进行，请稍候")
            return
        }
        val taskId = System.nanoTime()
        val total = accounts.size
        // 展示顶部任务进度
        GlobalUiBus.showTaskProgress(taskId, title = "账号检测中", total = total, current = 0)
        checkTaskJob = viewModelScope.launch {
            var index = 0
            for (account in accounts) {
                // 更新进度到 index（从 0 开始展示）
                GlobalUiBus.updateTaskProgress(taskId, current = index)
                try {
                    // 串行等待单个检查完成
                    repository.onAccountCheck(account.accountID, account.platform).handleResult(
                        onSuccess = { result ->
                            val now = System.currentTimeMillis()
                            updateUiState { state ->
                                val newData = state.accountData.map {
                                    if (it.accountID == account.accountID) {
                                        it.copy(
                                            lastCheck = now,
                                            online = if (result.status == "ACTIVE") 2 else if (result.status == "BAN") 3 else 1
                                        )
                                    } else it
                                }
                                state.copy(accountData = newData)
                            }
                        },
                        onError = { error ->
                            Logger.d("检查出错=${error}")
                        },
                        onLoading = { /* 批量任务不使用全局 Loading，避免遮挡 */ }
                    )
                } catch (e: Exception) {
                    Logger.d("检查异常=${e.message}")
                }
                // 单个完成后更新进度
                index += 1
                GlobalUiBus.updateTaskProgress(taskId, current = index)
            }
            // 全部完成，隐藏进度提示
            GlobalUiBus.hideTaskProgress(taskId)
            GlobalUiBus.showToast("完成检测：$total 个账号")
        }
    }

    fun onCheckAccount(accounts:List<AccountEntry>,onBlock: (AccountEntry) -> Unit = {}){
        if (checkTaskJob?.isActive == true) {
            GlobalUiBus.showToast("有检查任务正在进行，请稍候")
            return
        }
        viewModelScope.launch {
            if(accounts.isNotEmpty()){
                val account = accounts[0]
                repository.onAccountCheck(account.accountID,account.platform).handleResult(
                onSuccess = { result ->
                    updateUiState { state ->
                        val now = System.currentTimeMillis()
                        val newData = state.accountData.map {
                            if(it.accountID == account.accountID){
                                it.copy(
                                    lastCheck = now,
                                    online = if(result.status == "ACTIVE") 2 else if(result.status == "BAN") 3 else 1
                                )
                            } else it
                        }
                        onBlock(newData[0])
                        state.copy(
                            accountData = newData
                        )
                    }
                    GlobalUiBus.showToast(if(result.status == "ACTIVE") "存活中" else if(result.status == "BAN") "被封禁" else "待检测")
                },
                onError = { error ->
                    Logger.d("检查出错=${error}")
                },
                onLoading = {
                        if (it) GlobalUiBus.showLoading("检查中...")
                        else GlobalUiBus.hideLoading()
                    }
                )
            }

        }
    }

    /**
     * 搜索账号
     */
    private var searchJob: Job? = null
    fun onSearchAccount(keyword:String){
        searchJob?.cancel()
        searchJob = viewModelScope.launch {
            val q = keyword.trim()
            if (q.isEmpty()) {
                updateUiState { it.copy(accountData = emptyList(), isMore = false) }
                return@launch
            }
            delay(250)
            try {
                val result = accountStore.search(q, page = 1, pageSize = 500)
                updateUiState { it.copy(searchAccountData = result.items) }
            } catch (e: Exception) {
                GlobalUiBus.showToast("搜索失败：${e.message ?: "未知错误"}")
            }
        }
    }


    /**
     * 导出被选中的账号成Excel
     */
    fun onExportAccount(){
        val selected = uiState.value.accountData.filter { it.isSelect }
        if (selected.isEmpty()) {
            GlobalUiBus.showToast("请先选择要导出的账号")
            return
        }
        viewModelScope.launch {
            val uri = ExcelUtils.exportAccountsToCsv(appContext, selected)
            if (uri != null) {
                GlobalUiBus.showAlertDialog(
                    title = "已导出到下载目录：${uri}",
                    positiveText = "查看文件",
                    negativeText = "知道了",
                    cancelable = true,
                    onConfirm = {
                        try {
                            val intent = Intent(Intent.ACTION_VIEW).apply {
                                setDataAndType(uri, "text/csv")
                                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            }
                            appContext.startActivity(intent)
                        } catch (e: Exception) {
                            try {
                                val downloadsIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply {
                                    addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                                }
                                appContext.startActivity(downloadsIntent)
                            } catch (e2: Exception) {
                                GlobalUiBus.showToast("无法打开文件，请在文件管理中查看：$uri")
                            }
                        }
                    }
                )
            } else {
                GlobalUiBus.showToast("导出失败，请重试")
            }
        }
     }

    /**
     * 编辑或新增单格账号
     */
    fun onAddOrEditAccount(account: AccountEntry){
        if(account.accountID.isEmpty()){
            GlobalUiBus.showToast("请输入账号ID")
        } else if(account.twoFa?.isEmpty() == true && account.cookie?.isEmpty() == true){
            GlobalUiBus.showToast("2FA秘钥与Cookie至少填一项")
        } else if (account.proxyHost?.isEmpty() != true && (
                    account.proxyPort?.isEmpty() == true ||
                    account.proxyAccount?.isEmpty() == true ||
                    account.proxyPassword?.isEmpty() == true
                )){
            GlobalUiBus.showToast("若要设置代理，请填写完整信息")
        } else {
            GlobalUiBus.showAlertDialog(
                title = "是否确认保存${account.platform}账号",
                positiveText = "确认",
                negativeText = "取消",
                cancelable = true,
                onConfirm = {
                    viewModelScope.launch {
                        val count = accountStore.upsert(account)
                        if(count > 0) {
                            GlobalUiBus.showAlertDialog(
                                title = "保存成功！",
                                positiveText = "继续添加",
                                negativeText = "知道了",
                                cancelable = true,
                                onCancel = {
                                    Navigator3.pop()
                                }
                            )
                        }
                    }
                }
            )
        }
    }

    /**
     * 根据账号ID获取数据库账号信息
     */
    fun getAccountById(id:String,onBlock: (AccountEntry) -> Unit){
        viewModelScope.launch {
           val account = accountStore.findByAccountId(id)
            account?.let { onBlock(it) }
        }
    }

    /**
     * 代理检查
     */
    fun proxyCheck(channel: String,protocol:String,host:String,port:String,username:String,password:String,block:(String) -> Unit = {}){
        viewModelScope.launch {
            repository.onProxyCheck(channel,protocol,host,port,username,password).handleResult(
                onSuccess = { result ->
                    block(JsonUtils.toJson(result))
                },
                onError = { error ->
                    GlobalUiBus.showToast("检测失败：${error.toUserMessage()}")
                    Logger.d("检查出错=${error}")
                },
                onLoading = { load ->
                    updateUiState { state ->
                        state.copy(
                            isCheckProxy = load
                        )
                    }
                }
            )
        }
    }

    fun proxyFormatStr(inputTxt:String,onBlock:(ProxyInfo) -> Unit){
        val text = inputTxt.trim()
        if(text.isEmpty()){
            GlobalUiBus.showToast("请输入或粘贴代理字符串")
            return
        }
        // 支持两种格式：
        // 1) 协议://用户名:密码@主机:端口号
        // 2) 协议://主机:端口号
        val regex = Regex("^([a-zA-Z][a-zA-Z0-9+\\-\\.]*?)://(?:(?:([^:@]+):([^@]+))@)?(\\[[^\\]]+\\]|[^:]+):(\\d+)\\s*$")
        val match = regex.find(text)
        if(match == null){
            GlobalUiBus.showToast("不能识别，可以手动添加")
            return
        }
        val scheme = match.groupValues[1]
        val username = match.groupValues.getOrNull(2)?.takeIf { it.isNotEmpty() }
        val password = match.groupValues.getOrNull(3)?.takeIf { it.isNotEmpty() }
        val hostRaw = match.groupValues[4]
        val host = if (hostRaw.startsWith("[") && hostRaw.endsWith("]")) hostRaw.substring(1, hostRaw.length - 1) else hostRaw
        val port = match.groupValues[5]
        val type = when (scheme.lowercase()) {
            "socks", "socks5" -> "Socks5"
            "http", "https" -> "HTTP"
            else -> scheme.uppercase()
        }
        val proxyInfo = ProxyInfo(
            proxyType = type,
            proxyHost = host,
            proxyPort = port,
            proxyAccount = username,
            proxyPassword = password,
        )
        onBlock(proxyInfo)
        GlobalUiBus.showToast("代理信息已识别并填充")
    }

    fun downFile(account: AccountEntry) {
        viewModelScope.launch(Dispatchers.IO) {
            if(account.authCode == null){
                GlobalUiBus.showToast("无认证令牌")
                return@launch
            }
            repository.getAccountAuthInfo(account).handleResult(
                onSuccess = { result ->
                    //解压认证文件
                    Logger.d("下载完成/解压${if(result == null) "失败" else "成功"}=${result.absoluteFile}")
                },
                onError = { error ->
                    Logger.d("获取失败=${error}")
                },
                onLoading = { load ->

                }
            )
        }
    }

}

data class AccountUiState(
    val isLoading: Boolean = false,
    val isMore: Boolean = false,
    val isSave: Boolean = false,
    val isCheckProxy: Boolean = false,
    val isAllAccount: Boolean = false,
    val v2FAResume: Boolean = true,
    val header: List<String> = emptyList(),
    val rows: List<List<String>> = emptyList(),
    val authCode: String = "",
    val progress: Float = 1f,
    val secondsLeft: Int = 30,
    val accountEntry: AccountEntry? = null,
    val accountData: List<AccountEntry> = emptyList(),
    val searchAccountData: List<AccountEntry> = emptyList(),
    val accountFilterParams: AccountFilterParams? = null,
)

/**
 *  UI 事件
 */
sealed interface AccountUiEvent {
    data class ShowError(val message: String) : AccountUiEvent
    data class ShowMessage(val message: String) : AccountUiEvent
}